/*
 * JSFConsole - Lightweight JSF Application Monitor
 * Copyright (C) 2009  Grzegorz Bugaj
 * http://www.gregbugaj.com
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
package com.gregbugaj.reflector.visitors;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.gregbugaj.jsfconsole.util.ReflectUtil;
import com.gregbugaj.reflector.filter.PropertyFilterStrategy;
/**
 * This class can't be instantiated
 * @author devil
 *
 */
public abstract class AbstractVisitor implements IVisitor{

	protected Map<String, ReferencedObject> references;
	protected JSFDumpContext context;

	public AbstractVisitor(){
		references=new HashMap<String, ReferencedObject>();
		context=new JSFDumpContext();
	}
	
	
	public JSFDumpContext getContext() {
		return context;
	}


	public void visit(final Object obj) {		
		visit(obj, null);
	}


	protected void visit(final Object obj, final Object fieldOrMethod) {
		//Base
		if(obj==null || (obj.getClass())== Object.class){
			return;		
		} 
		boolean isField=((null==fieldOrMethod)?Field.class:fieldOrMethod.getClass())==Field.class;
		//Fixme:Need to add support for methods
		if(!isField)return;

		Field fieldOrMethodTmp=(Field)fieldOrMethod;

		//Check field filtering strategy
		if(isField){ }
		if(fieldOrMethodTmp!=null){}

		String fieldName=fieldOrMethodTmp==null?"":fieldOrMethodTmp.getName();

		PropertyFilterStrategy properyFilterStrategy = context.getFilterStrategy();
		properyFilterStrategy.inspect(obj);

		if(properyFilterStrategy.isValidProperty(fieldName) == false){
			return;
		}						

		String referenceID=ReflectUtil.createRefId(obj);
		boolean isReference=references.containsKey(referenceID);

		if(!isReference){
			references.put(referenceID, new ReferencedObject(referenceID, obj));
		}
		//We want to display values for simple objects rather than references to them
		if(isReference && ReflectUtil.isSimpleType(obj)){
			isReference=false;
		}

		//System.out.println(obj+" === "+isReference+" ====== "+referenceID);
		//We are not keeping track of references if they are methods only for objects

		if(isReference && isField){
			visitReference(references.get(referenceID), (Field)fieldOrMethod);
		}else if(isReference && !isField){
			visitReference(references.get(referenceID), (Method)fieldOrMethod);
		}else if(!isField){
			visitMethod(obj, (Method)fieldOrMethod);
		}else if(ReflectUtil.isSimpleType(obj)){
			visitSimple(obj, (Field)fieldOrMethod);
		}else if(ReflectUtil.isCollection(obj)){
			visitCollection((Collection<?>)obj,(Field)fieldOrMethod);
		}
		else if(ReflectUtil.isArray(obj)){

			//byte[] b=byte[] cant be casted to Object[] a=(Object[])b;			
			visitArray(new ArrayWrapper(obj), (Field)fieldOrMethod);
		}
		else if(ReflectUtil.isMapType(obj)){
			visitMap((Map<?,?>)obj, (Field)fieldOrMethod);
		}
		else if(ReflectUtil.isEnum(obj)){
			visitEnum((Enum<?>)obj, (Field)fieldOrMethod);
		}
		else{
			visitComplex(obj, (Field)fieldOrMethod);
		}

	}	

	//private static void c(Object o){System.out.println(o);}
}
